﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.XPath;
using Furion;

namespace YShop
{
    public static class Utils
    {
        #region Guid
        private static DateTime dt1970 = new DateTime(1970, 1, 1);
        private static ThreadLocal<Random> rnd = new ThreadLocal<Random>(() => new Random());
        private static readonly int __staticMachine = ((0x00ffffff & Environment.MachineName.GetHashCode()) +
#if NETSTANDARD1_5 || NETSTANDARD1_6
			1
#else
            AppDomain.CurrentDomain.Id
#endif
            ) & 0x00ffffff;
        private static readonly int __staticPid = Process.GetCurrentProcess().Id;
        private static int __staticIncrement = rnd.Value.Next();
        /// <summary>
        /// 生成类似Mongodb的ObjectId有序、不重复Guid
        /// </summary>
        /// <returns></returns>
        public static string NewGuid()
        {
            var now = DateTime.Now;
            var uninxtime = (int)now.Subtract(dt1970).TotalSeconds;
            int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff;
            var rand = rnd.Value.Next(0, int.MaxValue);
            var guid = $"{uninxtime.ToString("x8").PadLeft(8, '0')}{__staticMachine.ToString("x8").PadLeft(8, '0').Substring(2, 6)}{__staticPid.ToString("x8").PadLeft(8, '0').Substring(6, 2)}{increment.ToString("x8").PadLeft(8, '0')}{rand.ToString("x8").PadLeft(8, '0')}";
            return Guid.Parse(guid).ToString();
        }
        #endregion

        #region Validation
        /// <summary>
        /// 检查是否为Null并抛出异常
        /// </summary>
        /// <param name="data"></param>
        public static void CheckNull(object data)
        {
            if (data == null)
                new ArgumentNullException("参数不合法！");
        }
        /// <summary>
        /// 是否可空类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;
        #endregion

        #region Type

        /// <summary>
        /// 获取枚举注释
        /// </summary>
        /// <returns>Dict：key=属性名，value=注释</returns>
        static int _CodeBaseNotSupportedException = 0;
        public static Dictionary<string, string> GetEnumNamesComment(Type type)
        {
            return LocalGetComment(type, 0);

            Dictionary<string, string> LocalGetComment(Type localType, int level)
            {
                if (localType.Assembly.IsDynamic) return null;
                //动态生成的程序集，访问不了 Assembly.Location/Assembly.CodeBase
                var regex = new Regex(@"\.(dll|exe)", RegexOptions.IgnoreCase);
                var xmlPath = regex.Replace(localType.Assembly.Location, ".xml");
                if (File.Exists(xmlPath) == false)
                {
                    if (_CodeBaseNotSupportedException == 1) return null;
                    try
                    {
                        if (string.IsNullOrEmpty(localType.Assembly.Location)) return null;
                    }
                    catch (NotSupportedException) //NotSupportedException: CodeBase is not supported on assemblies loaded from a single-file bundle.
                    {
                        Interlocked.Exchange(ref _CodeBaseNotSupportedException, 1);
                        return null;
                    }
                    xmlPath = regex.Replace(localType.Assembly.Location, ".xml");
                    if (xmlPath.StartsWith("file:///") && Uri.TryCreate(xmlPath, UriKind.Absolute, out var tryuri))
                        xmlPath = tryuri.LocalPath;
                    if (File.Exists(xmlPath) == false) return null;
                }

                var dic = new Dictionary<string, string>();
                var sReader = new StringReader(File.ReadAllText(xmlPath));
                using (var xmlReader = XmlReader.Create(sReader))
                {
                    XPathDocument xpath = null;
                    try
                    {
                        xpath = new XPathDocument(xmlReader);
                    }
                    catch
                    {
                        return null;
                    }
                    var xmlNav = xpath.CreateNavigator();

                    var className = $"{localType.Namespace}.{localType.Name}";
                    var node = xmlNav.SelectSingleNode($"/doc/members/member[@name='T:{className}']/summary");
                    if (node != null)
                    {
                        var comment = node.InnerXml.Trim(' ', '\r', '\n', '\t');
                        dic.Add(localType.Name, comment); //class注释
                    }

                    var names = localType.GetEnumNames();
                    foreach (var name in names)
                    {
                        node = xmlNav.SelectSingleNode($"/doc/members/member[@name='F:{className}.{name}']/summary");
                        if (node == null)
                        {
                            continue;
                        }
                        var comment = node.InnerXml.Trim(' ', '\r', '\n', '\t');
                        if (string.IsNullOrEmpty(comment)) continue;

                        dic.Add(name, comment);
                    }
                }
                return dic;
            }
        }

        #endregion

    }
}
